# -*- coding:utf-8 -*-
#! /bin/python

# there is no tkFileDialog in python 3.*
# in python 3.* you should use tkinter instead of Tkinter
import tkinter as tk
from tkinter import ttk,messagebox
from tkinter import filedialog
#from ttk import *
#import messagebox
import DuplicateScan
import DuplicatedDirList
#import tkFileDialog
import os, _thread, shutil
from threading import Timer
import markduplicated
import my_file_op
import ProgressStatus,my_log,my_file_op,FileTree
import time

import ProjectManager

#reload(sys)
#sys.setdefaultencoding("utf-8")

project_manager = ProjectManager.ProjectManager()

item_prefix="     "

root = tk.Tk()
root.geometry("956x680")


fr = tk.Frame()


def on_update_left_paths_func(event):
    list_box_left_paths.delete(0,tk.END)
    for p in project_manager.old_file_paths:
        list_box_left_paths.insert(tk.END, p)
    pass

list_box_left_paths = tk.Listbox(fr, height=7, width=40, selectmode=tk.BROWSE)
list_box_left_paths.bind('<<InsertItem>>', on_update_left_paths_func)
scrollbar_for_left_paths = tk.Scrollbar(fr)


def on_update_right_paths_func(event):
    list_box_right_paths.delete(0,tk.END)
    for p in project_manager.new_file_paths:
        list_box_right_paths.insert(tk.END, p)
    pass

list_box_right_paths = tk.Listbox(fr, height=7, width=40, selectmode=tk.BROWSE)
list_box_right_paths.bind('<<InsertItem>>', on_update_right_paths_func)
scrollbar_for_right_paths = tk.Scrollbar(fr)

scrl = tk.Scrollbar(root)
list_box_compare_result = tk.Listbox(root, height=40, selectmode=tk.MULTIPLE)
progressbar_var = tk.IntVar()
progressbar = ttk.Progressbar(root, orient="horizontal", mode="determinate", value=100.0,variable=progressbar_var)


def on_update_compare_item_func(event):
    list_box_compare_result.delete(0, tk.END)
    all_leaf_nodes = project_manager.diff_file_tree.get_leaf_nodes()
    # >  file to dir
    # <  dir to file
    # - del
    # + add
    # * changed
    for n in all_leaf_nodes:
        list_box_compare_result.insert(tk.END, n.get_data() + "   " + n.get_full_path())


def on_click_item_func(event):
    # print lb_duplicate_files.curselection()
    print ("%s"%(list_box_compare_result.get(tk.ACTIVE)))

list_box_compare_result.bind('<ButtonRelease-1>', on_click_item_func)
list_box_compare_result.bind('<<InsertItem>>', on_update_compare_item_func)
list_box_compare_result.configure(yscrollcommand = scrl.set)
scrl['command'] = list_box_compare_result.yview


def thread_progressbar_refresh():
    global progressbar_var
    p = ProgressStatus.get_rate()
    progressbar_var.set(p)

    if ProgressStatus.is_running():
        Timer(0.5, thread_progressbar_refresh).start()
    else:
        bn_scan["text"] = "scan"
        bn_compare["text"] = "compare"
        bn_load_project["text"] = "load P"
        bn_save_project["text"] = "save P"


def thread_scan(no,useless):

    exculdefiles = [".", ".."]
    ProgressStatus.set_workload(1)
    ProgressStatus.start()
    # ##################file name and file size in filelist#######################

    filelist = []

    # remove duplicated base paths
    basepaths = project_manager.new_file_paths

    my_log.debug(str(len(basepaths)) + " dirs" + "\n")
    # --------------------------------
    # 遍历目录把路径保存在 filelist中
    # --------------------------------
    for _dir in basepaths:
        _filelist = my_file_op.get_files_from_dir(_dir, exculdefiles)
        filelist.extend(_filelist)

    filelist = my_file_op.remove_same_path_of_file_in_list(filelist)
    my_log.debug(str(len(filelist)) + " files in all" + "\n")

    cached_filetree = FileTree.FileTree()
    cachefile = my_file_op.CacheFile("my_zhangli_file_cache.txt")
    cached_filetree.load(cachefile.get_to_file())
    my_log.debug("my_zhangli_file_cache.txt loaded\n")

    filetree = FileTree.FileTree()

    all_file_size = 0
    for f in filelist:
        try:
            all_file_size += os.path.getsize(f)
        except OSError as e:
            my_log.debug("Get file info failed:" + f + "\n")
            continue
    ProgressStatus.set_workload(all_file_size)
    my_log.debug("size in all: " + my_file_op.human_readablity_size(all_file_size) + "\n")

    for f in filelist:
        if ProgressStatus.is_running() == False:
            break
        try:
            fsize = os.path.getsize(f)
            ftime = os.path.getmtime(f)
        except OSError as e:
            my_log.debug("Get file info failed:" + f + "\n")
            continue
        n = cached_filetree.get_node(f)

        if n is None or n.data.startswith(str(fsize) + "-" + str(ftime)) is False:
            md5 = my_file_op.get_md5_of_file(f)
            if md5 is None:
                continue
            data = str(fsize) + "-" + str(ftime) + "-" + md5
            cached_filetree.apply_path(f, data)
        else:
            assert n.data is not None and len(n.data) >= 32
            data = n.data
            ProgressStatus.process(fsize)

        filetree.apply_path(f, data)

    cachefile.save_to_file(cached_filetree.dump())

    project_manager.new_file_tree = filetree

    # lb_duplicate_files.event_generate("<<InsertItem>>", when="tail")
    ProgressStatus.stop()
    _thread.exit_thread()


def bn_scan_dir_func():
    if list_box_right_paths.size() == 0:
        my_log.debug("no path to scan\n")
        return
    if ProgressStatus.is_running():
        ProgressStatus.stop()
        return

    bn_scan["text"] = "stop"
    ProgressStatus.set_workload(1)
    ProgressStatus.start()

    #dirs = []
    # for i in range(0, list_box_right_paths.size()):
    #     dirs.append(list_box_right_paths.get(i))
    _thread.start_new_thread(thread_scan, (1, None))
    # start refresh timter
    Timer(0.5, thread_progressbar_refresh).start()


def thread_save_project(no, dir_name):
    project_manager.change_dir(dir_name)
    project_manager.save()

    ProgressStatus.stop()
    _thread.exit_thread()


def bn_save_project_func():
    if ProgressStatus.is_running():
        my_log.debug("scan under running"+"\n")
        return

    dir_name = tk.filedialog.askdirectory()
    if dir_name == "":
        return

    dir_name = dir_name + "/" + time.strftime("%Y%m%d%H%M%S")

    bn_save_project["text"] = "saving P"
    ProgressStatus.set_workload(1)
    ProgressStatus.start()

    _thread.start_new_thread(thread_save_project, (1, dir_name))
    #start refresh timter
    Timer(0.5, thread_progressbar_refresh).start()
    # save_to = os.path.dirname(os.path.realpath(__file__))+ os.path.sep + time.strftime("%Y%m%d%H%M%S") + ".zsync"
    #save_to = time.strftime("%Y%m%d%H%M%S") + ".zsync"
    #save_to = tkFileDialog.asksaveasfilename(**{"title":"Save scan result","initialfile":save_to,"defaultextension":".zsync","filetypes":[("zsync",".zsync")]})

    my_log.debug("save project to " + dir_name+"\n")
    # lb_duplicate_files.event_generate("<<InsertItem>>",when="tail")


def on_click_item_for_del_dir_func(event):
    if len(list_box_right_paths.curselection()) == 0:
        return
    list_box_right_paths.delete(list_box_right_paths.curselection())
    project_manager.new_file_paths = list(list_box_right_paths.get(0, tk.END))
    pass

list_box_right_paths.bind('<ButtonRelease-1>', on_click_item_for_del_dir_func)
scrollbar_for_right_paths['command'] = list_box_right_paths.yview


def bn_add_dir_func():
    dir = tk.filedialog.askdirectory()
    if dir is None or dir == "":
        return

    if my_file_op.is_sub_or_parent_dir(project_manager.new_file_paths, dir) is False:
        project_manager.new_file_paths.append(dir)
        list_box_right_paths.event_generate("<<InsertItem>>", when="tail")
    else:
        my_log.debug("sub or paranet dir is illegal: " + dir+"\n")


def bn_switch_func():
    if ProgressStatus.is_running():
        return

    # f = tkFileDialog.askopenfile(mode='r',**{"defaultextension":".zsync","filetypes":[("zsync",".zsync")]})
    # if f is None:
    #     return

    project_manager.switch()

    list_box_left_paths.event_generate("<<InsertItem>>", when="tail")
    list_box_right_paths.event_generate("<<InsertItem>>", when="tail")

def bn_del_func():
    if ProgressStatus.is_running():
        return

    del_count = len(list_box_compare_result.curselection())
    if messagebox.askokcancel("Delete","Del " + str(del_count)+ " files, are you sure?") is False:
        return

    for i in list_box_compare_result.curselection():
        _path = list_box_compare_result.get(i)
        _path = _path.split(" ",1)
        _type = _path[0].strip()
        _path = _path[1].strip()
        try:
            if _type == "+":
                project_manager.del_file_from_new_and_diff_filetree(_path)
                #print ("delete %s"%(_path.encode("utf8")),end="")
                print("delete %s" % (_path), end="")
        except OSError as e:
            #print ("error %s"%(_path.encode("utf8")),end="")
            print("error %s" % (_path), end="")

    list_box_compare_result.event_generate("<<InsertItem>>", when="tail")


def thread_compare(no,dirs):

    project_manager.compare()
    list_box_compare_result.event_generate("<<InsertItem>>", when="tail")
    ProgressStatus.stop()
    _thread.exit_thread()


def bn_compare_func():
    bn_compare["text"] = "stop"
    ProgressStatus.set_workload(1)
    ProgressStatus.start()

    _thread.start_new_thread(thread_compare, (1,""))
    #start refresh timter
    Timer(0.5, thread_progressbar_refresh).start()


def thread_load_project(no, dir_name):
    print("load project %s"%dir_name)
    project_manager.change_dir(dir_name)
    project_manager.load()

    list_box_left_paths.event_generate("<<InsertItem>>", when="tail")
    list_box_right_paths.event_generate("<<InsertItem>>", when="tail")

    ProgressStatus.stop()
    _thread.exit_thread()


def bn_load_project_func():
    bn_load_project["text"] = "loading P"
    ProgressStatus.set_workload(1)
    ProgressStatus.start()
    dir_name = tk.filedialog.askdirectory()
    _thread.start_new_thread(thread_load_project, (1, dir_name))
    #start refresh timter
    Timer(0.5, thread_progressbar_refresh).start()
    pass

bn_add_dir = ttk.Button(fr,text="Add DIR",command=bn_add_dir_func)
bn_scan = ttk.Button(fr,text="scan",command=bn_scan_dir_func)
bn_save_project = ttk.Button(fr, text="save P", command=bn_save_project_func)
bn_compare = ttk.Button(fr,text="compare",command=bn_compare_func)
bn_load_project = ttk.Button(fr, text="load P", command=bn_load_project_func)
bn_switch = ttk.Button(fr, text="Remote<=>Local", command=bn_switch_func)
bn_delete = ttk.Button(fr, text="del", command=bn_del_func)

list_box_left_paths.grid(row=0, column = 0, rowspan=5, sticky=tk.W + tk.E)
scrollbar_for_left_paths.grid(row=0, column = 1, rowspan=5, sticky=tk.W + tk.E + tk.N + tk.S)

bn_switch.grid(row=0, column = 2, rowspan=5, sticky=tk.W + tk.E, padx=0)

list_box_right_paths.grid(row=0, column = 3, rowspan=5, sticky=tk.W + tk.E)
scrollbar_for_right_paths.grid(row=0, column = 4, rowspan=5, sticky=tk.W + tk.E + tk.N + tk.S)


bn_add_dir.grid(row=0,column = 5,sticky=tk.W+tk.E,padx=0)
bn_scan.grid(row=1,column = 5,sticky=tk.W+tk.E,padx=0)
bn_load_project.grid(row=2, column = 5, sticky=tk.W + tk.E, padx=0)
bn_save_project.grid(row=3,column = 5,sticky=tk.W+tk.E,padx=0)
bn_compare.grid(row=4, column = 5, sticky=tk.W + tk.E, padx=0)

bn_delete.grid(row=0, column = 6, sticky=tk.W + tk.E, padx=0)

fr.pack(side=tk.TOP,fill=tk.X,expand=tk.TRUE)
progressbar.pack(side=tk.TOP,fill=tk.X)
list_box_compare_result.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
scrl.pack(side=tk.RIGHT,fill=tk.Y)

root.mainloop()